package compiler.mine;

import compiler.Dfa;
import compiler.Nfa;
import compiler.NfaPair;

import java.util.*;

/**
 * Created by szj on 2017/2/3.
 */
public class DfaConstructor {

    private NfaPair pair;
    private Interpreter interpreter;
    private List<Dfa> dfas = new ArrayList<>();
    //使用二维数组表示DFA有限状态自动机
    private int[][] dfaStateTransformTable = new int[MAX_DFA_STATE_COUNT][ASCII_COUNT];

    //假定DFA状态机节点数不会超过254个
    private static final int MAX_DFA_STATE_COUNT = 20;

    private static final int ASCII_COUNT = 128;

    private static final int STATE_FAILURE = -1;


    public DfaConstructor(NfaPair pair, Interpreter interpreter) {
        this.pair = pair;
        this.interpreter = interpreter;
        initDfaStateTransformTable();
    }

    public int[][] convertNfaToDfa() {
        Set<Nfa> nfas = new HashSet<>();
        nfas.add(pair.startNode);
        nfas = interpreter.eClosure(nfas);
        Dfa dfa = Dfa.constructDfa(nfas);
        dfas.add(dfa);
        int dfasTableRow = 0;
        int nextStatus;

        System.out.println("Create DFA start node:");
        printDfa(dfa);

        while (dfasTableRow < dfas.size()) {
            dfa = dfas.get(dfasTableRow);
            for (char c = 0; c < ASCII_COUNT; c++) {
                Set<Nfa> move = interpreter.move(dfa.nfaStates, c);
                if (move == null || move.isEmpty()) {
                    nextStatus = STATE_FAILURE;
                } else {
                    Set<Nfa> nfaClosure = interpreter.eClosure(move);
                    Dfa exist = getDfaIfExists(nfaClosure);
                    if (exist != null) {
                        System.out.println("already has nfas");
                        printDfa(exist);
                        nextStatus = exist.stateNum;
                    } else {
                        System.out.println("Create DFA node:");
                        Dfa nextDfa = Dfa.constructDfa(nfaClosure);
                        dfas.add(nextDfa);
                        printDfa(nextDfa);
                        nextStatus = nextDfa.stateNum;
                    }
                }
                System.out.println("Dfa from status: " + dfa.stateNum + " To status : " + nextStatus + " On char: " + c);
                if (nextStatus == 0) {
                    System.out.println();
                }
                dfaStateTransformTable[dfa.stateNum][c] = nextStatus;
            }
            System.out.println();
            dfasTableRow++;
        }
        System.out.println();
        printDfaTable(dfaStateTransformTable);
        return dfaStateTransformTable;
    }

    public static void printDfaTable(int[][] dfaStateTransformTable) {
        for (int i = 0; i < dfaStateTransformTable.length; i++) {
            System.out.print("dfa status : " + i);
            for (int j = 0; j < dfaStateTransformTable[i].length; j++) {
                String a = dfaStateTransformTable[i][j] != -1 ? dfaStateTransformTable[i][j] + "" : " ";
                System.out.print(" " + a);
            }
            System.out.println();
        }
    }

    private Dfa getDfaIfExists(Set<Nfa> nfas) {
        for (Dfa var : dfas) {
            if (var.nfaStates.equals(nfas)) {
                return var;
            }
        }
        return null;
    }

    private void printDfa(Dfa dfa) {
        Set<Nfa> nfas = dfa.nfaStates;
        Iterator<Nfa> iterator = nfas.iterator();
        System.out.print("Dfa status: " + dfa.stateNum + " its nfa status are: ");
        while (iterator.hasNext()) {
            Nfa nfa = iterator.next();
            System.out.print(nfa.getStateNum());
            if (iterator.hasNext()) {
                System.out.print(",");
            }
        }
        System.out.print("\n");
    }

    private void initDfaStateTransformTable() {
        for (int i = 0; i < MAX_DFA_STATE_COUNT; i++) {
            for (int j = 0; j < ASCII_COUNT; j++) {
                dfaStateTransformTable[i][j] = -1;
            }
        }
    }

    public int[][] getDfaStateTransformTable() {
        return dfaStateTransformTable;
    }

    public List<Dfa> getDfas() {
        return dfas;
    }
}
